package main

import (
	"fmt"

	"encoding/json"
	"github.com/henrylee2cn/faygo"
	"github.com/henrylee2cn/faygo/pongo2"
	"io"
	"os"
	"strings"
)

func main() {
	var app = faygo.New("JavaExam", "v1.0")
	app.Route(
		app.NewNamedGET("index", "/", home),
		app.NewNamedGET("index", "/admin", admin),
		app.NewNamedPOST("index", "/login", login),
		app.NewNamedGET("index", "/GetFAQ", GetFAQ),
		app.NewNamedPOST("index", "/answer", &Answer{}),
		app.NewNamedPOST("index", "/submit", &Submit{}),
		app.NewNamedPOST("index", "/State", State),
		app.NewNamedGET("index", "/exam", exam),

		app.NewNamedGET("index", "/manager", manager),
		app.NewNamedGET("index", "/manager_student", showstu),
		app.NewNamedGET("index", "/manager_faq", showfaq),
		app.NewNamedPOST("index", "/student", &Student{}),
		app.NewNamedPOST("index", "/faq", &Question{}),

		app.NewNamedStatic("...", "/uploads/images", "/uploads/images"),
	)
	go ORM()
	RegistCustomPongoFilter()
	Pages = make(map[int][]Question)
	Result = make(map[int]int)
	faygo.Run()
}

func RegistCustomPongoFilter() {
	pongo2.RegisterFilter("TATA", TATA)
}

func TATA(in, param *pongo2.Value) (*pongo2.Value, *pongo2.Error) {
	var m = in.Interface().(map[int]string)
	return pongo2.AsValue(m[param.Integer()]), nil
}

var home = faygo.HandlerFunc(func(ctx *faygo.Context) error {
	ctx.DestroySession()
	return ctx.Render(200, "view/home.tpl", faygo.Map{
		"err": "Just testing!",
	})
})

var admin = faygo.HandlerFunc(func(ctx *faygo.Context) error {
	return ctx.Render(200, "view/admin.tpl", faygo.Map{
		"err": "Just testing!",
	})
})

func (tmpObj *Admin) Serve(ctx *faygo.Context) error {
	fmt.Println("管理登录:", tmpObj)
	if exist, err := engine.Exist(tmpObj); exist && err == nil {
		return ctx.Render(200, "view/index.tpl", faygo.Map{})
	} else {
		fmt.Println("不存在管理员", err)
		return home(ctx)
	}
}

var exam = faygo.HandlerFunc(func(ctx *faygo.Context) error {
	if ctx.GetSession("num") == nil {
		return home(ctx)
	}
	pid := ctx.GetSession("num").(int)

	var stu Student
	engine.ID(int64(pid)).Get(&stu)
	if stu.Effect == 1 {
		ctx.SetSession("num", int(stu.Id))
		if len(stu.Image) == 0 {
			stu.Image = "upload/images/default.png"
		} else {
			stu.Image = "upload/images/" + stu.Image
		}
		count, err := engine.Count(new(Question))
		fmt.Println("题目数量:", count, err)
		return ctx.Render(200, "view/exam.tpl", faygo.Map{
			"list": make([]int, count),
			"info": stu,
			"memo": "考生报名时须如实、准确提交所有报考信息，完成缴费后，所有信息不得更改，并将打印在准考证上。考生在考试时须携带与准考证信息一致的有效身份证件原件参加考试。",
		})
	} else {
		return ctx.Render(200, "view/exam.tpl", faygo.Map{
			"memo": "你已提交过试卷!",
		})
	}
})

var login = faygo.HandlerFunc(func(ctx *faygo.Context) error {
	name := ctx.Param("name")
	password := ctx.Param("password")
	option := ctx.Param("option")
	if len(name) > 4 && len(password) > 4 && (option == "student" || option == "admin") {
		if option == "student" {
			var stu = Student{Ident: name, Password: password}
			if exist, err := engine.Exist(&stu); exist && err == nil {
				i, e := engine.Get(&stu)
				fmt.Println("获取学生信息:", i, e, stu)
				ctx.SetSession("num", int(stu.Id))
				return ctx.JSON(200, faygo.Map{
					"info": stu,
				})
			} else {
				fmt.Println("不存在学生", err)
				return ctx.JSON(200, faygo.Map{
					"error": "学员账号或密码错误",
				})
			}
		} else if option == "admin" {
			var admin = Admin{Name: name, Password: password}
			if exist, err := engine.Exist(&admin); exist && err == nil {
				i, e := engine.Get(&admin)
				fmt.Println("获取管理员信息:", i, e, admin)
				return ctx.JSON(200, faygo.Map{})
			} else {
				fmt.Println("不存在管理员", err)
				return ctx.JSON(200, faygo.Map{
					"error": "管理员账号或密码错误",
				})
			}
		} else {
			fmt.Println("缺少OPTION值~")
			return ctx.JSON(200, faygo.Map{
				"error": "缺少OPTION值",
			})
		}
	} else {
		fmt.Println("格式不对~")
		return ctx.JSON(200, faygo.Map{
			"error": "错误登录信息",
		})
	}
})

var showstu = faygo.HandlerFunc(func(ctx *faygo.Context) error {
	var stulist []Student
	engine.OrderBy("id").Find(&stulist)
	return ctx.Render(200, "view/student.tpl", faygo.Map{"list": stulist})
})

var showfaq = faygo.HandlerFunc(func(ctx *faygo.Context) error {
	var faq []Question
	engine.OrderBy("id").Find(&faq)
	return ctx.Render(200, "view/faq.tpl", faygo.Map{"list": faq})
})

var manager = faygo.HandlerFunc(func(ctx *faygo.Context) error {
	stu_count, _ := engine.Count(new(Student))
	faq_count, _ := engine.Count(new(Question))
	return ctx.Render(200, "view/manager.tpl", faygo.Map{
		"stu": stu_count,
		"faq": faq_count,
	})
})

func (tmpObj *Student) Serve(ctx *faygo.Context) error {
	option := ctx.Param("option")
	if option == "CCC" && len(tmpObj.Ident) == 18 {
		img, _, rerr := ctx.FormFile("img")
		fmt.Println("接收文件检查", rerr)
		if rerr == nil {
			_, err := os.Open("upload/images/" + tmpObj.Ident + ".png")
			if err == nil {
				os.Remove("upload/images/" + tmpObj.Ident + ".png")
			}
			f, ferr := os.Create("upload/images/" + tmpObj.Ident + ".png")
			fmt.Println("创建文件检查:", ferr)
			defer f.Close()
			io.Copy(f, img)
			tmpObj.Image = tmpObj.Ident + ".png"
		}
		row, err := engine.Omit("create_time", "id").InsertOne(tmpObj)
		fmt.Println("CCC", row, err, tmpObj)
		return ctx.JSON(200, faygo.Map{
			"row": row,
			"err": err,
		})
	} else if option == "UUU" && len(tmpObj.Ident) == 18 {
		var str = "image"
		img, _, rerr := ctx.FormFile("img")
		fmt.Println("接收文件检查", rerr)
		if statInterface, ok := img.(Stat); ok {
			fileInfo, _ := statInterface.Stat()
			fmt.Printf("上传文件的大小为: %d\n", fileInfo.Size())
		}
		if rerr == nil {
			_, err := os.Open("upload/images/" + tmpObj.Ident + ".png")
			if err == nil {
				os.Remove("upload/images/" + tmpObj.Ident + ".png")
			}
			f, ferr := os.Create("upload/images/" + tmpObj.Ident + ".png")
			fmt.Println("创建文件检查:", ferr)
			defer f.Close()
			io.Copy(f, img)
			tmpObj.Image = tmpObj.Ident + ".png"
			str = ""
		}
		tmpObj.Image = tmpObj.Ident + ".png"
		row, err := engine.ID(tmpObj.Id).AllCols().Omit("create_time", str).Update(tmpObj)
		fmt.Println("UUU", row, err, tmpObj)
		return ctx.JSON(200, faygo.Map{
			"row": row,
			"err": err,
		})
	} else if option == "RRR" {
		if tmpObj.Id != 0 {
			exist, err := engine.ID(tmpObj.Id).Get(tmpObj)
			fmt.Println("RRR", exist, err, tmpObj)
			return ctx.JSON(200, faygo.Map{
				"obj": tmpObj,
				"err": err,
			})
		}
	} else if option == "DDD" {
		row, err := engine.ID(tmpObj.Id).Delete(tmpObj)
		fmt.Println("UUU", row, err, tmpObj)
		return ctx.JSON(200, faygo.Map{
			"row": row,
			"err": err,
		})
	}
	return ctx.JSON(200, faygo.Map{"err": "CURD操作失败!"})
}

func (tmpObj *Question) Serve(ctx *faygo.Context) error {
	option := ctx.Param("optional")
	fmt.Println(option)
	if option == "CCC" {
		img, _, rerr := ctx.FormFile("img")
		fmt.Println("接收文件检查", rerr)
		if statInterface, ok := img.(Stat); ok {
			fileInfo, _ := statInterface.Stat()
			fmt.Printf("上传文件的大小为: %d\n", fileInfo.Size())
		}
		if rerr == nil {
			_, err := os.Open(fmt.Sprintf("upload/question/%d.png", int(tmpObj.Id)))
			if err == nil {
				os.Remove(fmt.Sprintf("upload/question/%d.png", int(tmpObj.Id)))
			}
			f, ferr := os.Create(fmt.Sprintf("upload/question/%d.png", int(tmpObj.Id)))
			fmt.Println("创建文件检查:", ferr)
			defer f.Close()
			io.Copy(f, img)
			tmpObj.Image = fmt.Sprintf("%d.png", int(tmpObj.Id))
			fmt.Println("文件名:", tmpObj.Image)
		}
		opt := ctx.FormParams("opt")
		var lll map[int]string
		json.Unmarshal([]byte(opt[0]), &lll)
		for k, v := range lll {
			if len(strings.TrimSpace(v)) < 2 {
				delete(lll, k)
			} else {
				fmt.Println("WHJY WHYWHWYW", v, len(strings.TrimSpace(v)))
			}
		}
		tmpObj.Option = lll
		tmpObj.Desc = strings.TrimSpace(tmpObj.Desc)
		tmpObj.Reason = strings.TrimSpace(tmpObj.Reason)
		tmpObj.Desc = strings.Replace(tmpObj.Desc, " ", "", -1)
		tmpObj.Reason = strings.Replace(tmpObj.Reason, " ", "", -1)
		fmt.Println("保存目标名:", tmpObj.Image)
		row, err := engine.Omit("create_time", "id").InsertOne(tmpObj)
		fmt.Println("CCC", row, err, tmpObj)
		return ctx.JSON(200, faygo.Map{
			"row": row,
			"err": err,
		})
	} else if option == "UUU" {
		var str = "image"
		img, _, rerr := ctx.FormFile("img")
		fmt.Println("接收文件检查", rerr)
		if statInterface, ok := img.(Stat); ok {
			fileInfo, _ := statInterface.Stat()
			fmt.Printf("上传文件的大小为: %d\n", fileInfo.Size())
		}
		if rerr == nil {
			_, err := os.Open(fmt.Sprintf("upload/question/%d.png", int(tmpObj.Id)))
			if err == nil {
				os.Remove(fmt.Sprintf("upload/question/%d.png", int(tmpObj.Id)))
			}
			f, ferr := os.Create(fmt.Sprintf("upload/question/%d.png", int(tmpObj.Id)))
			fmt.Println("创建文件检查:", ferr)
			defer f.Close()
			io.Copy(f, img)
			tmpObj.Image = fmt.Sprintf("%d.png", int(tmpObj.Id))
			fmt.Println("文件名:", tmpObj.Image)
			str = ""
		}
		opt := ctx.FormParams("opt")
		var lll map[int]string
		json.Unmarshal([]byte(opt[0]), &lll)
		for k, v := range lll {
			if len(strings.TrimSpace(v)) < 1 {
				delete(lll, k)
			} else {
				fmt.Println("WHJY WHYWHWYW", v, len(strings.TrimSpace(v)))
			}
		}
		tmpObj.Desc = strings.TrimSpace(tmpObj.Desc)
		tmpObj.Reason = strings.TrimSpace(tmpObj.Reason)
		tmpObj.Desc = strings.Replace(tmpObj.Desc, " ", "", -1)
		tmpObj.Reason = strings.Replace(tmpObj.Reason, " ", "", -1)
		tmpObj.Option = lll
		fmt.Println("保存目标名:", tmpObj.Image)
		row, err := engine.ID(tmpObj.Id).AllCols().Omit("create_time", str).Update(tmpObj)
		fmt.Println("UUU", row, err, tmpObj)
		return ctx.JSON(200, faygo.Map{
			"row": row,
			"err": err,
		})
	} else if option == "RRR" {
		exist, err := engine.ID(tmpObj.Id).Get(tmpObj)
		fmt.Println("RRR", exist, err, tmpObj)
		return ctx.JSON(200, faygo.Map{
			"obj": tmpObj,
			"err": err,
		})
	} else if option == "DDD" {
		row, err := engine.ID(tmpObj.Id).Delete(tmpObj)
		fmt.Println("UUU", row, err, tmpObj)
		return ctx.JSON(200, faygo.Map{
			"row": row,
			"err": err,
		})
	}
	return ctx.JSON(200, faygo.Map{"err": "CURD操作失败!"})
}

var GetFAQ = faygo.HandlerFunc(func(ctx *faygo.Context) error {
	var faq []Question
	pid := ctx.GetSession("num").(int)
	p, exist := Pages[pid]
	if !exist {
		engine.OrderBy("rand()").Find(&faq)
		for i := range faq {
			if len(faq[i].Image) > 0 {
				faq[i].Image = "/upload/question/" + faq[i].Image
			}
		}
		Pages[pid] = faq
		fmt.Println("初始化试卷", pid)
	} else {
		faq = p
	}
	return ctx.JSON(200, faq)
})

type Answer struct {
	Qid int `param:"<in:formData><required><range:0:100>"`
	Ans int `param:"<in:formData><required><range:-1:7>"`
}

func (tmpObj *Answer) Serve(ctx *faygo.Context) error {
	pid := ctx.GetSession("num").(int)
	Pages[pid][tmpObj.Qid].Answer = tmpObj.Ans
	fmt.Printf("答题: 考生号:%d,题号:%d,选择:%d\n", pid, tmpObj.Qid, tmpObj.Ans)
	var result bool
	if Pages[pid][tmpObj.Qid].Answer == Pages[pid][tmpObj.Qid].Correct {
		result = true
	}
	return ctx.JSON(200, faygo.Map{
		"success": true,
		"correct": result,
	})
}

//交卷
type Submit struct {
	Pid int `param:"<in:formData><required>"`
}

func (tmpObj *Submit) Serve(ctx *faygo.Context) error {
	pid := ctx.GetSession("num").(int)
	var pass bool
	Result[pid] = 0
	for _, v := range Pages[pid] {
		if v.Answer == v.Correct {
			Result[pid]++
		}
	}
	if Result[pid] > 90 {
		pass = true
	}
	var str string
	if pass {
		str = "恭喜你通过了考试!"
	} else {
		str = "很遗憾,你尚未通过考试,仍需继续努力!"
	}
	var stu = Student{Effect: 0}
	row, err := engine.Id(int64(pid)).Cols("effect").Update(&stu)
	defer func() {
		if row == 1 && err == nil {
			delete(Pages, pid)
			delete(Result, pid)
		}
	}()
	fmt.Println("检查学生状态:", row, err)
	return ctx.JSON(200, faygo.Map{
		"success": true,
		"point":   Result[pid],
		"obj":     tmpObj,
		"pass":    pass,
		"str":     str,
	})
}

var State = faygo.HandlerFunc(func(ctx *faygo.Context) error {
	pid := ctx.GetSession("num").(int)
	var stu Student
	engine.ID(int64(pid)).Get(&stu)
	return ctx.JSON(200, faygo.Map{
		"state": stu.Effect,
	})

})

var Pages map[int][]Question
var Result map[int]int

// 获取文件大小的接口
type Size interface {
	Size() int64
}

// 获取文件信息的接口
type Stat interface {
	Stat() (os.FileInfo, error)
}
